package com.ikingtech.platform.service.authorization;

import com.ikingtech.framework.sdk.base.model.BatchParam;
import com.ikingtech.framework.sdk.cache.constants.CacheConstants;
import com.ikingtech.framework.sdk.context.exception.FrameworkException;
import com.ikingtech.framework.sdk.context.security.Me;
import com.ikingtech.framework.sdk.department.api.DeptApi;
import com.ikingtech.framework.sdk.department.model.DeptBasicDTO;
import com.ikingtech.framework.sdk.enums.system.role.DataScopeTypeEnum;
import com.ikingtech.framework.sdk.role.api.RoleApi;
import com.ikingtech.framework.sdk.user.api.UserApi;
import com.ikingtech.framework.sdk.user.model.UserDTO;
import com.ikingtech.framework.sdk.user.model.UserDeptDTO;
import com.ikingtech.framework.sdk.user.model.UserRoleDTO;
import com.ikingtech.framework.sdk.utils.Tools;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author tie yan
 */
@Service
@RequiredArgsConstructor
public class AuthorizationService {

    private final StringRedisTemplate redisTemplate;

    private final UserApi userApi;

    private final RoleApi roleApi;

    private final DeptApi deptApi;

    public String grant(String userId) {
        UserDTO user = this.userApi.detail(userId).getData();
        if (null == user) {
            throw new FrameworkException("userNotFound");
        }
        Map<String, List<String>> dataScopeMap = this.grant(user.getRoles(), user.getDepartments());
        Map<String, String> cacheDataScopeMap = new HashMap<>(dataScopeMap.size());
        dataScopeMap.forEach((menuId, dataScopeCodes) -> cacheDataScopeMap.put(menuId, Tools.Coll.join(dataScopeCodes)));
        if (Tools.Coll.isNotBlankMap(dataScopeMap)) {
            //缓存菜单信息到redis
            this.redisTemplate.opsForHash().putAll(CacheConstants.userAuthDetailsFormat(user.getUsername(), Me.tenantCode()), cacheDataScopeMap);
        }
        return user.getId();
    }

    private Map<String, List<String>> grant(List<UserRoleDTO> roles, List<UserDeptDTO> departments) {
        // 根据角色id查询每个角色关联的菜单id
        Map<String, List<String>> menuIdMap = this.roleApi.mapMenuIdsByIds(BatchParam.build(Tools.Coll.convertList(roles, UserRoleDTO::getRoleId))).getData();
        if (Tools.Coll.isBlankMap(menuIdMap)) {
            return new HashMap<>(0);
        }

        // key是menuId,value是部门全路径集合的map
        Map<String, List<String>> dataScopeMap = new HashMap<>(roles.size());
        //如果角色列表中有一个角色的数据范围是“全部数据”,则给每个菜单都赋予所有单位权限
        if (roles.stream().anyMatch(role -> role.getRoleDataScopeType().equals(DataScopeTypeEnum.ALL))) {
            List<String> deptFullPaths = Tools.Coll.distinct(this.deptApi.fullPathAll().getData());
            menuIdMap.values().stream().flatMap(Collection::stream).distinct().forEach(menuId -> dataScopeMap.put(menuId, deptFullPaths));
        } else {
            // 轮询所有的角色
            roles.forEach(role -> {
                // 根据角色信息和用户所属单位获取该角色的单位权限
                List<String> currentRoleDataScopeCodes = this.parseDataScopeCode(role.getRoleDataScopeType(), role.getRoleDataScopeCodes(), departments);
                if (Tools.Coll.isNotBlank(currentRoleDataScopeCodes) && menuIdMap.containsKey(role.getRoleId())) {
                    // 轮询该角色对应的菜单
                    menuIdMap.get(role.getRoleId()).forEach(menuId -> {
                        // 为每个菜单加单位权限
                        List<String> allDataScopeCodes = dataScopeMap.getOrDefault(menuId, new ArrayList<>());
                        allDataScopeCodes.addAll(currentRoleDataScopeCodes);
                        dataScopeMap.put(menuId, allDataScopeCodes);
                    });
                }
            });
        }
        return dataScopeMap;
    }

    private List<String> parseDataScopeCode(DataScopeTypeEnum dataScopeType, List<String> roleDataScopeCodes, List<UserDeptDTO> departments) {
        List<String> dataScopeCodes = new ArrayList<>();
        //指定部门的数据,且部门编号不为空,根据部门编号获取所有的部门全路径
        if (DataScopeTypeEnum.DEFINE.equals(dataScopeType) && Tools.Coll.isNotBlank(roleDataScopeCodes)) {
            List<String> deptFullPaths = this.deptApi.listFullPathByIds(BatchParam.build(roleDataScopeCodes)).getData();
            if (Tools.Coll.isNotBlank(deptFullPaths)) {
                dataScopeCodes.addAll(deptFullPaths);
            }
        }
        //本部门及以下数据,且用户所属部门列表不为空,获取用户所属部门及所有的子部门的全路径
        if (DataScopeTypeEnum.DEPT_WITH_CHILD.equals(dataScopeType) && Tools.Coll.isNotBlank(departments)) {
            List<DeptBasicDTO> allDepartments = this.deptApi.listSubInfoAllByParentIds(BatchParam.build(Tools.Coll.convertList(departments, UserDeptDTO::getDeptId))).getData();
            List<String> deptFullPaths = this.deptApi.listFullPathByOrgIds(BatchParam.build(Tools.Coll.convertList(allDepartments, DeptBasicDTO::getOrgId))).getData();
            if (Tools.Coll.isNotBlank(deptFullPaths)) {
                dataScopeCodes.addAll(deptFullPaths);
            }
        }
        //本部门的数据,且用户所属部门列表不为空,直接拿所属部门的全路径
        if (DataScopeTypeEnum.DEPT.equals(dataScopeType) && Tools.Coll.isNotBlank(departments)) {
            List<DeptBasicDTO> allDepartments = this.deptApi.listInfoByIds(BatchParam.build(Tools.Coll.convertList(departments, UserDeptDTO::getDeptId))).getData();
            List<String> deptFullPaths = this.deptApi.listFullPathByOrgIds(BatchParam.build(Tools.Coll.convertList(allDepartments, DeptBasicDTO::getOrgId))).getData();
            if (Tools.Coll.isNotBlank(deptFullPaths)) {
                dataScopeCodes.addAll(deptFullPaths);
            }
        }
        return dataScopeCodes;
    }
}
